import Api from '../request/api.js';
import path from 'path';
import fs from 'fs';
import {
  md5,
  sleep,
  log,
  randomInt,
  simulateMove,
  subHallName,
  getRandomString,
  getHallId,
} from '../utils/utils.js';
import process from 'process';
import dotenv from 'dotenv';
import puppeteer from 'puppeteer-extra';
import StealthPlugin from 'puppeteer-extra-plugin-stealth';
import { Page, Browser } from 'puppeteer-core';
import dayjs from 'dayjs';
import { PayType, TicketChannel } from '../constant/index.js';
import axios from 'axios';
import { generatePayToken } from '../utils/PhoenixRSAEncryption.js';

const ua =
  'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36 MicroMessenger/6.8.0(0x16080000) NetType/WIFI MiniProgramEnv/Mac MacWechat/WMPF MacWechat/3.8.10(0x13080a10) XWEB/1227';

// const waitSelectTime = 300000; // 选座等待时间
dotenv.config();
puppeteer.use(StealthPlugin());
const {
  EXECUTABLE_PATH,
  CINEMA_ACC_ID,
  CINEMA_LINKID,
  CINEMA_CODE = 'Master',
  CINEMA_TOKEN,
  CINEMA_DATA,
  CINEMA_ACCOUNT,
  CINEMA_PWD,
  CINEMA_CHANNEL,
  ENV,
} = process.env;
const CinemaData = CINEMA_DATA && JSON.parse(CINEMA_DATA);

const API = new Api();

export default class Phoenix {
  /**
   * @type  {Page} page
   */
  page;
  
  requestErr = '';

  /**
   * @type  {Browser} browser
   */
  browser;

  // cinema_code
  cinemaCode = CINEMA_CODE;

  // token
  reqtToken =
    'e30f3cbbe850701ec359e6322a3f6c5f_1754751429465%3B4cae8a890c98321d3376fbbefb7930a6';
  token = '';
  expiresAt = 0;
  refreshToken = '';

  // 接口请求头
  requestHeader = {
    'User-Agent': ua,
    Accept: 'application/json',
    'Accept-Language': 'zh-CN,zh;q=0.9',
    'Content-Type': 'application/x-www-form-urlencoded',
    'Sec-Fetch-Dest': 'empty',
    'Sec-Fetch-Mode': 'cors',
    'Sec-Fetch-Site': 'same-site',
    'x-tap': 'wx',
    xweb_xhr: '1',
    referer: 'https://servicewechat.com/wxe1675b6f92d3b921/8/page-frame.html',
  };

  // 请求exbody
  requestbody = {};

  // 会员卡信息
  vipCardInfo = {
    cardBalance: 0, // 会员卡余额
    cardNo: '', // 会员卡号
    cardName: '', // 会员名称
    vipAmount: 0, // 订单预计消费金额
  };

  // 订单信息
  order = {
    state: '',
    ticketCode: '',
    paramDetail: {}, // 订单详情参数
  };

  // 楼层
  sectionIndex = 0;

  // 账户ID
  accountId = 0;

  // 影院ID
  cinemaLinkId = 0;
  // 用户TID sid更新使用
  userTid = '';
  userSid = '';
  //   mobile = '';

  tokenUser = {};

  // 订单ID
  orderId = '';

  // 锁定订单ID
  lockOrderId = '';

  // data
  dataJson = null;

  getPage() {
    return this.page;
  }

  getBrowser() {
    return this.browser;
  }

  // 页面接口请求
  async requestPageApi(path, data, first = true) {
    const c = this.reqtToken;
    let postData = {
      ...this.requestbody,
      ...data,
    };
    postData = JSON.stringify(postData);

    const timestamp = Date.now();
    const appKey = 12574478;
    const sign = md5(
      c.split('_')[0] + '&' + timestamp + '&' + appKey + '&' + postData
    );
    let url = `https://mtop.yuekeyun.com/h5/${path.toLocaleLowerCase()}/1.0/2.0/?jsv=2.4.12&appKey=${appKey}&t=${timestamp}&c=${c}&sign=${sign}&v=1.0&type=originaljson&dataType=json&timeout=20000&url=${path}&api=${path}&_bx-m=1`;
    if (this.expiresAt > Date.now() && this.token) {
      url += `&sid=${this.token}`;
    }
    const bxHeaders = await this.page.evaluate((requestHeader) => {
      //人机识别模块，只需初始化一次
      if (typeof AWSC !== 'undefined' && AWSC !== null) {
        let uabModule;
        let webUmidToken;
        AWSC.use('uab', function (state, uab) {
          if (state === 'loaded') {
            uabModule = uab;
          }
        });
        //设备指纹模块，得到设备token，只需初始化一次
        AWSC.use('um', function (state, um) {
          if (state === 'loaded') {
            um.init(
              {
                appName: 'lark-cinemaprod',
              },
              function (initState, result) {
                if (initState === 'success') {
                  webUmidToken = result.tn;
                }
              }
            );
          }
        });
        try {
          requestHeader['bx-ua'] = uabModule.getUA();
          requestHeader['bx-umidtoken'] = webUmidToken;
        } catch (_e) {}
      }
      return requestHeader;
    }, this.requestHeader);

    // 通过axios请求
    const res = await axios
      .request({
        url,
        method: 'POST',
        data: 'data=' + encodeURIComponent(postData),
        headers: bxHeaders,
      })
      .catch((e) => {
        log('请求接口失败 ' + JSON.stringify(e));
        return false;
      })
      .then((resp) => {
        return resp.data;
      });
    if (res.c && first) {
      this.reqtToken = res.c;
      return this.requestPageApi(path, data, false);
    }

    const retMsg = res.ret[0];
    console.log(path + '接口返回信息' + retMsg);
    if (
      path !== 'mtop.alipic.lark.account.authn.refresh' &&
      (retMsg.includes('FAIL_SYS_TOKEN_EXOIRED::令牌过期') ||
        retMsg.includes('FAIL_SYS_SESSION_EXPIRED::Session过期')) &&
      first &&
      (await this.doRefreshToken(this.cinemaCode))
    ) {
      // 登录失效
      return await this.requestPageApi(path, data, false);
    } else if (!retMsg.startsWith('SUCCESS')) {
      this.requestErr = retMsg;
      log(
        `接口返回失败 ${url} 参数${JSON.stringify(postData)} ${JSON.stringify(
          res
        )} [${path}]`
      );
      return false;
    }
    return res.data;
  }

  setOrderState(state, ticketCode = '', paramDetail = null) {
    if (ticketCode) {
      this.order.ticketCode = ticketCode;
    }
    if (paramDetail) {
      this.order.paramDetail = paramDetail;
    }

    this.order.state = state;

    fs.writeFileSync(
      './orderJson/' + this.dataJson.sn + '.json',
      JSON.stringify(this.order, null, 2),
      {
        flag: 'w',
      },
      (err) => {
        if (err) {
          console.error('订单状态写入', err);
        }
      }
    );
  }

  // 判断是否已经出票
  async isTicket(dataJson) {
    const filePath = './orderJson/' + dataJson.sn + '.json';
    if (fs.existsSync(filePath)) {
      // 存在说明已经出过票
      const buffer = fs.readFileSync(filePath);
      let ticketCode = '';
      const fileContent = buffer.toString();
      let paramDetail = {};
      if (fileContent) {
        const orderState = JSON.parse(fileContent);
        if (orderState.state === 'paySuc') {
          paramDetail = orderState.paramDetail;
          // 支付成功, 但是获取详情失败
          const orderDetail = await this.requestPageApi(
            'mtop.alipic.lark.order.detail.get',
            paramDetail.paramDetail
          ).then((res) => res && res.order && res.order.ticket);
          if (!orderDetail) {
            throw new Error('获取订单详情失败[RETRY]');
          }
          ticketCode = orderDetail && orderDetail.pickupCode;
          this.setOrderState('success', ticketCode, paramDetail);
        } else {
          ticketCode = orderState.ticketCode;
        }
      }
      return { payType: paramDetail.payType || 0, ticketCode };
    }

    return false;
  }

  // 订单开始 初始化
  async orderStart(dataJson) {
    this.requestErr = '';
    // 设置账户ID
    this.accountId = dataJson.accountId;

    // 设置影院LinkId
    this.cinemaLinkId = CINEMA_LINKID;
    // 设置影院code`
    this.cinemaCode = CINEMA_CODE;
    this.requestbody = {
      leaseCode: this.cinemaCode.toLowerCase().split('_')[0],
      channelCode: this.cinemaCode,
    };
    // 录入token
    this.setLoginInfo(CINEMA_TOKEN);

    this.order = {
      state: '',
      ticketCode: '',
      paramDetail: {},
    };
    this.vipCardInfo = {
      cardBalance: 0, // 会员卡余额
      cardNo: '', // 会员卡号
      cardName: '',
      vipAmount: 0, // 订单预计消费金额
    };
    this.sectionIndex = dataJson.sectionId - 1;
    this.dataJson = dataJson;
    this.lockOrderId = '';
    this.orderId = '';
  }

  // 页面初始化
  async init() {
    // 初始化
    this.browser = await puppeteer.launch({
      executablePath: EXECUTABLE_PATH,
      //设置超时时间
      timeout: 15000,
      ignoreHTTPSErrors: true,
      devtools: ENV === 'local',
      headless: ENV === 'local' ? false : 'new',
      args: ['--no-sandbox', '--disable-plugins', '--disable-setuid-sandbox'],
    });
    const pages = await this.browser.pages();
    this.page = pages[0];
    await this.page.setGeolocation({ latitude: 31.23, longitude: 121.47 });
    await this.page.setUserAgent(ua);
    await this.page.setViewport({ width: 375, height: 700 });
    // 直接打开首页
    await this.page.goto(`https://h5lark.yuekeyun.com/location/index.html`, {
      waitUntil: 'networkidle0',
    });
  }

  // 刷新token
  async doRefreshToken(code = CINEMA_CODE) {
    // 刷新token
    const refreshToken = await this.requestPageApi(
      'mtop.alipic.lark.account.authn.refresh',
      {
        refreshToken: this.refreshToken,
        leaseCode: code.toLowerCase().split('_')[0],
        channelCode: code,
      }
    );
    if (refreshToken.refreshToken) {
      this.refreshToken = refreshToken.refreshToken;
      this.token = refreshToken.accessToken;
      this.expiresAt = refreshToken.accessTokenExAt;

      // 更新token
      await API.updateLoginToken(
        this.accountId,
        JSON.stringify({
          refreshToken: refreshToken.refreshToken,
          accessToken: refreshToken.accessToken,
          reqtToken: this.reqtToken,
          accessTokenExAt: refreshToken.accessTokenExAt,
        })
      );

      return true;
    }

    await API.authExpire(this.accountId);
    return false;
  }

  async selectShow() {
    // 解锁请求
    const bizData = await this.requestPageApi(
      'mtop.alipic.lark.schedule.filmschedules.get',
      {
        cinemaLinkId: this.cinemaLinkId,
        pageInit: true,
      }
    );
    if (!bizData) {
      return false;
    }

    // 获取场次
    const showTimestamp = this.dataJson.showTimestamp;
    const { showObj } = this.dataJson;
    const showList = [];
    for (const item of bizData.filmSchedules) {
      if (showObj.filmId !== item.filmId) {
        continue;
      }

      for (const dateMap of item.dateSchedules) {
        for (const show of dateMap.schedules) {
          if (Number(show.startTime) !== showTimestamp) {
            continue;
          }
          showList.push(show);
        }
      }
    }
    if (showList.length === 0) {
      return false;
    }
    if (showList.length > 1) {
      // 选择最早的场次
      const hn = subHallName(this.dataJson.hallName);
      for (const show of showList) {
        if (hn === subHallName(show.hallName)) {
          return show;
        }
      }
    }
    return showList[0];
  }

  // 获取座位信息
  async getSeat(selectShowRes) {
    // 等待选座
    const dataJson = this.dataJson;
    // 解锁请求
    const bizData = await this.requestPageApi(
      'mtop.alipic.lark.seat.scheduleseats.get',
      {
        cinemaLinkId: this.cinemaLinkId,
        scheduleId: selectShowRes.scheduleId,
        scheduleKey: selectShowRes.scheduleKey,
        pageInit: true,
      }
    );
    if (!bizData || !bizData.sectionSeats) {
      log(
        '场次座位表获取失败 ' +
          ' sid:' +
          this.token +
          ' ' +
          JSON.stringify(this.reqtToken),
        'error'
      );
      throw new Error('场次座位表获取失败[RETRY]');
    }

    // 获取场次
    const seatList = [];
    const { seats } = bizData.sectionSeats[this.sectionIndex];
    // 座位转成 [[11,2],[11,3]]
    const seatListArr = this.dataJson.seatList.map((i) =>
      i.match(/[\d|A-Za-z]+/g).map((d) => (isNaN(Number(d)) ? d : Number(d)))
    );
    for (const seat of seats) {
      const seatArr = seat.seatName
        .match(/[\d|A-Za-z]+/g)
        .map((d) => (isNaN(Number(d)) ? d : Number(d)));
      for (const item of seatListArr) {
        if (seatArr[0] == item[0] && seatArr[1] == item[1]) {
          if (seat.status != 'N') {
            throw new Error(
              `座位已被锁定 ${this.dataJson.from} ${this.dataJson.cinemaName}`
            );
          }
          seatList.push(seat);
        }
      }
    }
    if (seatList.length < dataJson.seatList.length) {
      throw new Error('座位未找到');
    }
    return seatList;
  }

  // 锁定座位
  /**
   * [{"areaId":"0","columnNum":1,"rowName":"1","rowNum":1,"seatCode":"000000055841-1-1","seatName":"1排10座","status":"N","type":"N"}]
   */
  async lockSeat(showInfo, seatList) {
    // 锁定请求
    const lockBizData = await this.requestPageApi(
      'mtop.alipic.lark.seat.scheduleseats.lock',
      {
        cinemaLinkId: this.cinemaLinkId,
        scheduleId: showInfo.scheduleId,
        scheduleKey: showInfo.scheduleKey,
        seatCodes: JSON.stringify(seatList.map((seat) => seat.seatCode)),
      }
    );
    if (lockBizData && lockBizData.lockOrderId) {
      // 成功锁座
      log('座位锁定成功 ' + JSON.stringify(lockBizData));
      this.lockOrderId = lockBizData.lockOrderId;
    } else {
      // 锁座失败
      log('座位锁定请求失败 ' + JSON.stringify(lockBizData), 'error');
      throw new Error('锁座失败[RETRY]');
    }
  }

  // 获取订单信息
  async submitOrderInfo(showInfo, seatList, cinemaInfo) {
    log(
      '提交订单 ' + JSON.stringify(showInfo) + ' ' + JSON.stringify(cinemaInfo)
    );

    // 初始化会员卡
    if (
      cinemaInfo.settle_type == PayType.BALANCE ||
      cinemaInfo.settle_type == PayType.LESS_COST
    ) {
      await this.requestPageApi('mtop.alipic.lark.card.initmembercards.get', {
        cinemaLinkId: this.cinemaLinkId,
        leaseCode: this.cinemaCode.toLowerCase().split('_')[0],
        channelCode: this.cinemaCode,
      });
    }

    // 获取会员&优惠券信息
    const seats = JSON.stringify(
      seatList.map((seat) => ({
        seatCode: seat.seatCode,
        areaId: seat.areaId,
      }))
    );
    const privilegeBizData = await this.requestPageApi(
      'mtop.alipic.lark.order.ticketorder.settle',
      {
        cinemaLinkId: this.cinemaLinkId,
        scheduleId: showInfo.scheduleId,
        scheduleKey: showInfo.scheduleKey,
        seats,
        lockOrderId: this.lockOrderId,
        pageInit: true,
      }
    );
    log('获取会员&优惠券信息 ' + JSON.stringify(privilegeBizData));
    if (!privilegeBizData) {
      throw new Error('获取会员&优惠券信息失败[RETRY]');
    }

    let payInfo;
    let payType;
    if (cinemaInfo.settle_type == PayType.BALANCE) {
      // 使用会员卡余额
      payInfo = await this.useVipBalance(privilegeBizData, cinemaInfo);
      payType = PayType.BALANCE;
    } else if (
      cinemaInfo.settle_type == PayType.E_TICKET ||
      cinemaInfo.settle_type == PayType.COUPON
    ) {
      // 仅使用优惠券 (电子券)
      payInfo = await this.useCoupon(
        privilegeBizData,
        seatList,
        showInfo,
        cinemaInfo
      );
      payType = PayType.E_TICKET;
    } else {
      // 哪个成本低用哪个
      let vipPayInfo = null;
      let couponPayInfo = null;
      let eTicketPayInfo = null;
      if (cinemaInfo.vip_cost_ratio > 0) {
        vipPayInfo = await this.useVipBalance(
          privilegeBizData,
          cinemaInfo
        ).catch(() => null);
      }
      if (cinemaInfo.coupon_fee > 0 || cinemaInfo.e_ticket_fee > 0) {
        couponPayInfo = await this.useCoupon(
          privilegeBizData,
          seatList,
          showInfo,
          cinemaInfo
        ).catch(() => null);
      }
      log(
        '使用会员&优惠券信息 ' +
          JSON.stringify(vipPayInfo) +
          ' ' +
          JSON.stringify(couponPayInfo)
      );
      if (!vipPayInfo && !couponPayInfo) {
        throw new Error('无法选择支付方式');
      }

      let vipCost = 999999999;
      let couponCost = 999999999;
      if (vipPayInfo) {
        vipCost = vipPayInfo.cost;
      }
      if (couponPayInfo) {
        couponCost = couponPayInfo.cost;
      }

      // 支付方式 1优惠券 2vip 3券+vip
      if (vipCost <= couponCost) {
        payInfo = vipPayInfo;
        payType = PayType.BALANCE;
      } else {
        payInfo = couponPayInfo;
        payType = cinemaInfo.coupon_fee > 0 ? PayType.COUPON : PayType.E_TICKET;
      }
      log(`VIP成本:${vipCost} 优惠券成本:${couponCost} 类型:${payType}`);
    }

    log('更新成本 ' + JSON.stringify(payInfo));
    // 更新成本
    await API.updateOrderCost(this.dataJson.id, payInfo.cost / 100);

    // 判定成本与报价
    const sellAmount = this.dataJson.sellAmount * 100;
    log('判定成本与报价 ' + sellAmount + ' ' + payInfo.cost);
    // force_ticket是否强制出票
    if (
      sellAmount > 0 &&
      sellAmount <= payInfo.cost &&
      !cinemaInfo.force_ticket
    ) {
      throw new Error(
        `成本高于报价[类型${payType}-成本${payInfo.cost}-报价${sellAmount}]`
      );
    }

    // 创建订单
    const payCost = payInfo.cost;
    delete payInfo.cost;
    const closeOuterId = getRandomString(16);
    const createTicketOrder = await this.requestPageApi(
      'mtop.alipic.lark.order.ticketorder.create',
      {
        promotions: JSON.stringify(payInfo.promotions),
        payments: JSON.stringify(payInfo.payments),
        totalOriginalPrice: payInfo.totalOriginalPrice,
        totalPayAmount: payInfo.totalPayAmount,
        seats,
        cinemaLinkId: this.cinemaLinkId,
        phoneNumber: CINEMA_ACCOUNT,
        scheduleId: showInfo.scheduleId,
        scheduleKey: showInfo.scheduleKey,
        lockOrderId: this.lockOrderId,
        closeOuterId,
        outerId: closeOuterId,
      }
    );
    log('创建订单 ' + JSON.stringify(createTicketOrder));
    if (!createTicketOrder) {
      throw new Error('订单创建失败[TRY]');
    }

    // 设置订单ID 用户订单回滚
    this.orderId = createTicketOrder.orderId;

    // 设置订单状态
    const paramDetail = {
      orderId: createTicketOrder.orderId,
      pageInit: true,
    };

    // 写入订单状态防止出现异常
    this.setOrderState('paySuc', '', {
      payType,
      paramDetail,
    });

    log('获取订单详情 ' + JSON.stringify(paramDetail));
    const orderDetail = await this.requestPageApi(
      'mtop.alipic.lark.order.detail.get',
      paramDetail
    ).then((res) => res && res.order && res.order.ticket);
    if (!orderDetail) {
      throw new Error('获取订单详情失败[RETRY]');
    }

    // 设置订单状态
    this.setOrderState('success', orderDetail.pickupCode, {
      payType,
      paramDetail,
    });

    orderDetail['payCost'] = payCost;
    orderDetail['payType'] = payType;
    return orderDetail;
  }

  // 获取订单原始金额
  getOriginPrice(privilegeBizData) {
    return privilegeBizData.privileges.find(
      (p) => p.privilegeType === 'ORIGINAL_PRICE'
    )['privilegeTotalPrice'];
  }

  getOrderCoupon() {
    const commonCoupon = [];
    const payments = [];
    let countCouponNum = 0;
    for (const coupon of sortArr) {
      // 出票需要额外的券
      let extened = 0;
      if (commonCoupon.length < length) {
        // 是否需要选择多张
        if (coupon.exchangeQuantity - 1 > 0) {
          if (extened === 0) {
            extened = coupon.exchangeQuantity - 1;
            length = length + extened;
          } else {
            extened = extened - 1;
          }
        }
        if (coupon.concreteProductType === 'COMMON') {
          // 通用券必须标注使用方式
          commonCoupon.push({
            couponCode: coupon.couponCode,
            concreteProductType: 'TICKET',
          });

          payments.push({
            payMethod: 'COUPON',
            couponCodeParams: `${coupon.couponCode}-${coupon.couponType}-TICKET`,
          });
        } else {
          commonCoupon.push({
            couponCode: coupon.couponCode,
          });

          payments.push({
            payMethod: 'COUPON',
            couponCodeParams: `${coupon.couponCode}-${coupon.couponType}`,
          });
        }
      } else {
        countCouponNum += 1;
      }
    }

    return { payments, commonCoupon };
  }

  // 使用优惠券
  async useCoupon(privilegeBizData, seatList, selectShowRes, cinemaInfo) {
    const { coupons = [] } = privilegeBizData;
    let { length } = seatList;
    const commonCoupon = [];
    const payments = [];
    let countCouponNum = 0;
    // 过滤加排序
    const couponListArr = coupons.filter(
      (coupon) =>
        coupon.isAvailable === 'Y' &&
        coupon.isOptional === 'Y' &&
        coupon.couponType === 'EXCHANGE_COUPON'
    );
    const sortArr = couponListArr
      .sort((a, b) => a.endTime - b.endTime) // 过期时间从小到大排序
      .sort((a, b) => b.couponValue - a.couponValue); // 折扣值从大到小排序
    // 将券分类
    const couponTypeMap = {};
    for (const coupon of sortArr) {
      if (!couponTypeMap[coupon.templateCode]) {
        couponTypeMap[coupon.templateCode] = [];
      }
      couponTypeMap[coupon.templateCode].push(coupon);
    }
    // 分类之后还需要按折扣值从大到小排序
    const couponTypeArr = Object.values(couponTypeMap).sort(
      (a, b) => b[0].couponValue - a[0].couponValue
    );

    for (const coupons of couponTypeArr) {
      if (coupons.length < length) {
        continue;
      }
      for (const coupon of coupons) {
        // 出票需要额外的券
        let extened = 0;
        if (commonCoupon.length < length) {
          // 是否需要选择多张
          if (coupon.exchangeQuantity - 1 > 0) {
            if (extened === 0) {
              extened = coupon.exchangeQuantity - 1;
              length = length + extened;
            } else {
              extened = extened - 1;
            }
          }
          if (coupon.couponType === 'EXCHANGE_COUPON') {
            // 通用券必须标注使用方式
            commonCoupon.push({
              promotionType: 'COUPON',
              promoCode: coupon.couponCode,
              productType: coupon.productType,
            });

            // payments.push({
            //   payMethod: 'COUPON',
            //   couponCodeParams: `${coupon.couponCode}-${coupon.productType}-TICKET`,
            // });
          } else {
            commonCoupon.push({
              couponCode: coupon.couponCode,
              productType: coupon.productType,
            });

            // payments.push({
            //   payMethod: 'COUPON',
            //   couponCodeParams: `${coupon.couponCode}-${coupon.productType}`,
            // });
          }
        } else {
          countCouponNum += 1;
        }
      }
    }

    if (length > commonCoupon.length) {
      throw new Error('优惠券不足');
    }

    // 查询券优惠
    const seatsList = seatList.map((seat) => ({
      seatCode: seat.seatCode,
      areaId: seat.areaId,
    }));
    const queryDiscount = await this.requestPageApi(
      'mtop.alipic.lark.order.ticketorderpromo.settle',
      {
        cinemaLinkId: this.cinemaLinkId,
        scheduleId: selectShowRes.scheduleId,
        scheduleKey: selectShowRes.scheduleKey,
        seats: JSON.stringify(seatsList),
        lockOrderId: this.lockOrderId,
        promotions: JSON.stringify(commonCoupon),
      }
    );
    if (!queryDiscount || !queryDiscount.settlement) {
      log('++++++++凤凰优惠券 ' + JSON.stringify(couponTypeMap));
      throw new Error('优惠券折扣查询失败');
    }

    // 会员卡余额补充
    const privilegeTotalPrice = Number(
      queryDiscount.settlement.totalDiscountedPrice
    );
    if (privilegeTotalPrice !== 0) {
      throw new Error('需要补充付款,但未找到可支付会员卡');
      //   if (!privilegeBizData.cards || !Array.isArray(privilegeBizData.cards)) {
      //     throw new Error('需要补充付款,但未找到可支付会员卡');
      //   }

      //   let getCard = false;
      //   for (const card of privilegeBizData.activityAndCardPromotions) {
      //     if (
      //       card.canChargeFlg === 'Y' &&
      //       Number(card.balance) >=
      //         privilegeTotalPrice + Number(cinemaInfo.frozen_balance)
      //     ) {
      //       getCard = true;
      //       payments.push({
      //         payMethod: 'CARD',
      //         payCardNumber: card['cardNo'],
      //       });
      //       break;
      //     }
      //   }
      //   if (!getCard) {
      //     throw new Error('券抵扣余额不足');
      //   }
    }

    let cost = 9999999;
    if (cinemaInfo.coupon_fee > 0) {
      cost = cinemaInfo.coupon_fee * commonCoupon.length * 100;
      if (privilegeTotalPrice > cost) {
        throw new Error(
          `补充金额大于预设成本bc${privilegeTotalPrice}-cost${cost}`
        );
      }
      // 更新券数
      await API.updateCouponNum(countCouponNum);
    } else {
      cost =
        privilegeTotalPrice +
        cinemaInfo.e_ticket_fee * commonCoupon.length * 100;
      // 更新券数
      await API.updateETicketNum(countCouponNum);
    }
    // 上传券
    const vercodes = commonCoupon.map((c) => c.couponCode);
    await API.uploadCoupon(
      couponListArr
        .filter((i) => !vercodes.includes(i.couponCode))
        .map((coupon) => ({
          type: 1,
          couponNo: coupon.couponCode,
          status: coupon.isAvailable === 'Y' ? 1 : 2,
          timeStart: coupon.startTime / 1000,
          timeEnd: coupon.endTime / 1000,
          couponVer: coupon.templateCode || '',
          couponName: coupon.couponName || '',
          couponInfo:
            (coupon.couponType || '') + '-' + (coupon.productType || ''),
        })),
      CINEMA_ACC_ID,
      CINEMA_ACCOUNT,
      TicketChannel.PHOENIX_MP
    );

    // 优惠券支付抵扣金额
    const { promotions, payments: paymentsArr } = queryDiscount.settlement;
    const promotionsMap = {};
    for (const promotion of promotions) {
      promotionsMap[promotion.promoCode] = promotion.discountedAmount;
    }
    const payment = paymentsArr.find((p) => p.paymentType === 'MEMBER_CARD');
    if (!payment) {
      log('++++++++凤凰优惠券 ' + JSON.stringify(paymentsArr));
      throw new Error('无法找到可支付会员卡');
    }

    // 优惠券可以折扣全部金额
    return {
      payments: [
        {
          paymentType: payment.paymentType,
          payAmount: queryDiscount.settlement.totalDiscountedPrice,
          payCode: payment.cardNo,
          payToken: generatePayToken(cinemaInfo.vip_pwd),
        },
      ],
      promotions: commonCoupon.map((c) => {
        c.discountedAmount = promotionsMap[c.promoCode] || 0;
        return c;
      }),
      totalPayAmount: queryDiscount.settlement.totalDiscountedPrice,
      totalOriginalPrice: queryDiscount.settlement.totalOriginalPrice,
      cost, // 成本
    };
  }

  // 使用VIP余额
  async useVipBalance(privilegeBizData, cinemaInfo) {
    const { activityAndCardPromotions } = privilegeBizData;
    if (!activityAndCardPromotions || !activityAndCardPromotions.length) {
      throw new Error('无可用会员卡[RETRY]');
    }
    // 使用会员卡余额
    const cardMap = {}; // 会员卡Map
    let useCard = privilegeBizData.activityAndCardPromotions[0]; // 默认会员卡
    let minPrice = useCard['discountedPrice'];

    for (const card of privilegeBizData.activityAndCardPromotions) {
      cardMap[card['cardNo']] = card;
      // 选出价格最低的
      if (card['discountedPrice'] < minPrice) {
        minPrice = card['discountedPrice'];
        useCard = card;
      }
    }

    const { schedule, seats } = privilegeBizData;

    // 会员卡试算
    const seatsList = seats.map((seat) => ({
      seatCode: seat.seatCode,
      areaId: seat.areaId,
    }));
    const payCard = await this.requestPageApi(
      'mtop.alipic.lark.order.ticketorderpromo.settle',
      {
        cinemaLinkId: this.cinemaLinkId,
        scheduleId: schedule.scheduleId,
        scheduleKey: schedule.scheduleKey,
        seats: JSON.stringify(seatsList),
        lockOrderId: this.lockOrderId,
        promotions: JSON.stringify([
          {
            promotionType: useCard['promotionType'],
            promoCode: useCard['cardNo'],
          },
        ]),
      }
    );
    if (!payCard || !payCard.settlement || !payCard.settlement.promotions) {
      log('++++++++凤凰会员卡 ' + JSON.stringify(payCard));
      throw new Error('会员卡支付失败');
    }

    // 会员卡支付
    //[{"discountedAmount":100,"promoCode":"0010010000786","promotionType":"MEMBER_CARD"}]
    const promotion = payCard.settlement.promotions[0];
    // [{"paymentType":"MEMBER_CARD","payToken":"Wl1RRIl+9CE2TcZnsYuNwcoEkVnus9elOWVe5fMuAxp8kbtIZrEIuBvgBnUtZwsCaMd18ejZApAhhZp5hL6a63T2tKJ7LtCxRqXMxpMlkca45hLlfaF7XcIqhsEDH7XAeUJwL7s9B5fMsxEDBvZvnlfSElF5OGdrPo/w3elq04BsLM6x/CIP4Dn+xsaIIW2hER5psFHwz1uIBlF170zp/3pUeoGe2ILdRHS64O9gbywkKMSI69uWQyFa1xZt9KnIsS30C/mWVukefgGzx1TlIZ7ofs8aD63DTByQR31EFi+6uI8gJhYnM3CReI2a1uyQbHDdnRTEXv9f3pWvLSQgtg==","payCode":"20005060963X","payAmount":3290}]
    const payments = {
      paymentType: promotion.promotionType,
      payToken: generatePayToken(cinemaInfo.vip_pwd),
      payCode: promotion.promoCode,
      payAmount: payCard.settlement.totalDiscountedPrice,
    };

    return {
      payments,
      promotions: payCard.settlement.promotions,
      totalPayAmount: payCard.settlement.totalDiscountedPrice,
      totalOriginalPrice: payCard.settlement.totalOriginalPrice,
      cost: payCard.settlement.totalDiscountedPrice * cinemaInfo.vip_cost_ratio,
    };
  }

  // 解锁座位
  async unlockSeat() {
    if (this.lockOrderId) {
      // 解锁请求
      return await this.requestPageApi(
        'mtop.alipic.lark.seat.scheduleseats.unlock',
        {
          lockOrderId: this.lockOrderId,
          cinemaLinkId: this.cinemaLinkId,
        }
      );
    }
    return true;
  }

  // 订单取消
  async cancelOrder() {
    if (this.orderId) {
      // 解锁请求
      return await this.requestPageApi(
        'mtop.alipic.lark.own.order.cancelOrder',
        {
          orderId: this.orderId,
          cinemaLinkId: this.cinemaLinkId,
          orderType: 'TICKET',
        }
      );
    }
    return true;
  }

  // 获取订单信息
  async getOrderInfo() {
    const filePath = './orderJson/' + this.dataJson.sn + '.json';
    if (!fs.existsSync(filePath)) {
      return false;
    }
    const buffer = fs.readFileSync(filePath);
    const fileContent = buffer.toString();
    if (!fileContent) {
      return false;
    }
    const orderState = JSON.parse(fileContent);
    this.order = orderState;

    const orderDetailData = await this.requestPageApi(
      'mtop.alipic.lark.order.detail.get',
      orderState.paramDetail.paramDetail
    );
    if (!orderDetailData || orderDetailData.order.orderStatus !== 'SUCCESS') {
      log(
        '获取订单详情失败 ' +
          JSON.stringify(orderState) +
          ' ' +
          JSON.stringify(orderDetailData)
      );
      throw new Error(
        `订单的状态是${
          orderDetailData &&
          orderDetailData.order &&
          orderDetailData.order.orderStatus
        }[RETRY]`
      );
    }
    const orderDetail = orderDetailData.order;
    if (!orderDetail.ticket.pickupCode) {
      log('获取取票码失败[FAILED]:' + JSON.stringify(orderDetail));
      throw new Error('获取取票码失败[FAILED]');
    }

    let uploadLink = '';
    if (this.dataJson.screenshot) {
      // 上传截图
      uploadLink = await this.screenshotAndUpload(orderDetail);
    }
    this.setOrderState('success', orderDetail.ticket.pickupCode);

    return {
      orderId: orderDetailData.orderId,
      payType: orderState.paramDetail.payType,
      ticketCode: orderDetail.ticket.pickupCode,
      uploadLink,
    };
  }

  // 截图上传
  async screenshotAndUpload(detail) {
    const sep = path.sep;
    const ticket = detail.ticket;
    const data = {
      mobile: detail.phoneNumber,
      order_num: detail.orderId,
      movie_name: ticket.filmName,
      movie_img_url: ticket.filmPoster.replace('/', '/'),
      cinema_name: detail.cinemaName,
      hall_name: detail.hallName,
      seat_info: ticket.ticketSeats.map((seat) => seat.seatName).join(','),
      start_date: dayjs(ticket.startTime).format('YYYY-MM-DD'),
      start_time: dayjs(ticket.startTime).format('HH:mm:ss'),
      create_time: dayjs(detail.createTime).format('YYYY-MM-DD HH:mm:ss'),
      movie_format: ticket.filmVersion,
      msg: detail.msg,
      total_price: detail.settlement.ticketOriginalPrice,
      serial_number_1: ticket.pickupCode,
      serial_number_2: '',
      qrcode: ticket.pickupCode,
      ticket_code_type: 2,
      seats: ticket.ticketSeats.map((seat) => seat.seatName).join(','),
      message: '',
      tel: detail.phoneNumber,
    };
    // 本地路径
    const url = `file://${path.resolve(
      './'
    )}${sep}html${sep}order-detail.html?data=${encodeURIComponent(
      JSON.stringify(data)
    )}`;
    // 初始化
    const browser = await puppeteer.launch({
      executablePath: EXECUTABLE_PATH,
      //设置超时时间
      timeout: 15000,
      ignoreHTTPSErrors: true,
      headless: true,
      args: ['--disable-dev-shm-usage', '--no-sandbox', '--disable-plugins'],
    });
    const page = await browser.newPage();
    await page.goto(url);
    await page.setViewport({
      width: 375,
      height: 590,
    });

    // 截图
    const screenshotPath = './screenshot/' + this.dataJson.sn + '.png';
    await page.screenshot({
      path: screenshotPath,
    });
    await browser.close();

    // 上传截图
    return await API.uploadScreenshot(screenshotPath);
  }
  // 订单回退
  async orderRollback(dataJson) {
    log('回调订单状态,出票失败');
    // 订单锁定取消
    await API.orderRollback(dataJson.id, dataJson.msg).catch((e) =>
      console.error('取消订单请求异常', e)
    );
    // if (!this.order.state) {
    if (this.orderId) {
      // 创建订单成功 付款失败
      const cancelRes = await this.cancelOrder();
      log(`取消订单:${this.orderId} ${JSON.stringify(cancelRes)}`);
    } else if (this.lockOrderId) {
      // 仅仅是座位锁定成功 订单取消锁定
      const unlockRes = await this.unlockSeat();
      log(`解锁座位:${this.lockOrderId} ${JSON.stringify(unlockRes)}`);
    }
    // }

    return true;
  }

  // 更新账户场次信息
  async updateShow(task) {
    this.requestErr = '';
    // 设置账户ID
    this.accountId = task.account_id;
    // 设置影院code
    this.cinemaCode = task.cinemas_code;
    this.requestbody = {
      leaseCode: this.cinemaCode.toLowerCase().split('_')[0],
      channelCode: this.cinemaCode,
    };

    this.setLoginInfo(task.login_token);
    // 获取场次
    const bizData = await this.requestPageApi(
      'mtop.alipic.lark.schedule.filmschedules.get',
      {
        cinemaLinkId: task.cinema_data.linkid,
        pageInit: true,
      }
    );
    if (!bizData || !bizData.filmSchedules) {
      return false;
    }

    const filmMap = {};
    const filmIdMap = {};
    for (const film of bizData.filmSchedules) {
      filmMap[film.filmId] = [];
      for (const date of film.dateSchedules) {
        for (const schedule of date.schedules) {
          const showTime = dayjs(Number(schedule.startTime)).format(
            'YYYY-MM-DD HH:mm:ss'
          );
          filmIdMap[film.filmId] = schedule.filmName;
          // 厅ID
          const hallId = getHallId(schedule.hallName, task.cinema_data.linkid);
          filmMap[film.filmId].push({
            showTime,
            hallId,
            scheduleKey: schedule.scheduleKey,
            scheduleId: schedule.scheduleId,
            showNo: schedule.scheduleId,
            filmId: film.filmId,
            hallName: schedule.hallName,
            displayPrice:
              schedule.discountedPrice > 2500
                ? Math.round(
                    schedule.discountedPrice * task['vip_cost_ratio'],
                    2
                  )
                : schedule.discountedPrice, // 会员卡价格*系数
            originalStandardPrice: Number(schedule.originalPrice), // 标准价格
            standardPrice: Number(schedule.originalPrice) || 0, // 展示原价
            lowestPrice: 0, // 发行价
            ticketFee: 0, // 影院手续费
          });
        }
      }
    }

    // 把filmi转成filmName
    for (const filmId in filmMap) {
      const filmName = filmIdMap[filmId];
      filmMap[filmName] = filmMap[filmId];
      delete filmMap[filmId];
    }

    await API.updateShowlist(
      task.cinema_id,
      task.account_id,
      task.vip_price_status,
      filmMap
    );

    log(
      `${task['cinema_name']}[${task['cinemas_code']}] 场次数据更新成功[${task.account}] ${task.vip_cost_ratio}`
    );

    // 获取会员卡列表并通知后端
    const cardListRes = await this.requestPageApi(
      'mtop.alipic.lark.card.membercards.get',
      {
        cinemaLinkId: task.cinema_data.linkid,
        pageNumber: 1,
        pageSize: 20,
      }
    );
    if (
      cardListRes &&
      Array.isArray(cardListRes.memberCards) &&
      cardListRes.memberCards.length > 0
    ) {
      // 处理会员卡数据，包含活动信息和状态
      const cardData = cardListRes.memberCards.map((card) => {
        // 处理会员卡状态
        let status = 5; // 默认未知状态
        if (card.cardStatus === 'EXPIRE') {
          status = 2; // 过期
        } else if (card.cardStatus === 'ENABLED') {
          status = 1; // 正常使用
        }

        return {
          cardNumber: card.cardNo,
          cardName: card.cardName,
          cardStatus: status,
          balance: card.balance > 0 ? card.balance / 100 : 0,
          activeCardActivityList: card.activeCardActivityList || [],
        };
      });

      // 传输所有会员卡信息到后端
      const noticeRes = await API.noticeCardActivity(
        task.account_id,
        task.cinema_id,
        task.cinema_name,
        task.account,
        cardData
      );
      log(`通知会员卡信息:${JSON.stringify(noticeRes)}`);
    }
  }

  // 更新院线
  async updateCinemas(taskData) {
    this.requestErr = '';
    const bizData = await this.requestPageApi(
      'mtop.alipic.lark.cinema.citycinemas.get',
      {
        leaseCode: taskData.cinemas_code.toLowerCase().split('_')[0],
        channelCode: taskData.cinemas_code,
      }
    );
    if (!bizData || !bizData.cityCinemas) {
      return false;
    }

    const cityCinemasMap = [];
    for (const city of bizData.cityCinemas) {
      const cityInfo = {
        cityCode: city.cityCode,
        cityName: city.cityName,
      };

      for (const cinema of city.cinemas) {
        cityCinemasMap.push({
          ...cityInfo,
          ...cinema,
        });
      }
    }
    await API.updateCinemasData(
      taskData.id,
      taskData.vip_price_status,
      cityCinemasMap
    );

    log(
      `${taskData['cinemas_name']}[${taskData['cinemas_code']}] 院线数据更新成功`
    );
  }

  // 设置登录信息
  setLoginInfo(loginInfo = '{}') {
    try {
      const {
        reqtToken = '',
        accessToken = '',
        refreshToken = '',
        accessTokenExAt = 0,
      } = JSON.parse(loginInfo);
      this.token = accessToken;
      this.refreshToken = refreshToken;
      this.reqtToken = reqtToken;
      this.expiresAt = accessTokenExAt;
      return (
        reqtToken ||
        'e30f3cbbe850701ec359e6322a3f6c5f_1754751429465%3B4cae8a890c98321d3376fbbefb7930a6'
      );
    } catch (e) {}
  }
}
